home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 48 / Amiga Format CD48 (1999-12-13)(Future Publishing)(GB)(Track 1 of 2)[!][issue 2000-01].iso / -in_the_mag- / networking / crosspc / parpc04 / packet / src / parsubr.old < prev    next >
Text File  |  1993-09-01  |  27KB  |  956 lines

  1. ; Lowlevel routines for PARnet
  2. ; This is not the general PARnet-code. It will only work in combination
  3. ; with the PARnet packet driver.
  4. ;
  5. ; 03-JUN-93 First PC version of PARnet <S.A.Pechler@bdk.tue.nl>
  6. ; 03-JUL-93 Fixed bug when transferring odd number of bytes.
  7. ;           Optimized by putting some counters in registers <S.A.Pechler>
  8. ; 07-JUL-93 Bugfix: ES was not set when reading into overflow buffer.
  9. ;           Deleted leading underscores from all data labels.
  10. ;           Added ParReadV and ParWriteV functions.         <S.A.Pechler>
  11. ; 26-JUL-93 Changed this file to get it work with the PARnet packet
  12. ;           driver (combined ParReadV & ParWrite into ParRead & ParWrite).
  13. ;
  14. ;
  15. ;   PARALLEL PORT NETWORK LOW LEVEL ROUTINES
  16. ;
  17. ;  THE CABLE:  Connect D7-D0,SEL,POUT, and BUSY across,
  18. ;           Connect ACK to SEL both locally and across,
  19. ;           Connect GND lines indicated:
  20. ;
  21. ;   (2-9)   D7-D0   ------------    D7-D0  (data)
  22. ;   (12)    POUT    ------------    POUT   (~req)
  23. ;   (11)    BUSY    ------------    BUSY   (~ack)  PARNET INTERFACE
  24. ;   (13)    SEL     --+------+--    SEL    ( ctl)
  25. ;   (10)    ACK     -/        \-    ACK    ( irq)
  26. ;   (18-22) GND     ------------    GND
  27. ;
  28. ;
  29. ;   The interface has in it's standard layout no pullup resistors
  30. ;   on the lines. You don't need them when at least one Amiga is
  31. ;   connected on the line. When connecting PC's only, you need to
  32. ;   place 1 array of pullup resistors in the interface of the LAST
  33. ;   machine on the line. Such a pullup is made by connecting all the
  34. ;   lines shown in the cable above (except for GND) with 4.7 KOhm
  35. ;   resistors to +5 Volt.
  36. ;
  37. ;      +--+        +-----+          +--+           +--+
  38. ;      |PC|        |Amiga|          |PC|           |PC|
  39. ;      +--+        +-----+          +--+           +--+
  40. ;        |            |               |              |
  41. ;     +-----+         |            +-----+        +-----+    
  42. ;     |Iface|   .     |            |Iface|        |Iface|    
  43. ;     +-----+   .     |            +-----+        +-----+
  44. ;        |      |     |               |       .      | ++-+
  45. ;        \------+-----/               |       .      +-|PR|
  46. ;  PARnet cable between PC & Amiga    |       |      | +--+
  47. ;       (no resistors needed)         \-------+------/
  48. ;                                   PARnet cable between PC's
  49. ;                                   (add pullup resistors)
  50. ;
  51. ;   More than 2 machines on the same line are ofcourse possible.
  52. ;   Note that you can't connect more than 3 Amiga's (or 2 Amiga's
  53. ;   and 1 PC) without additional buffers, otherwise you'll blow up
  54. ;   your CIA's. The software can handle 254 machines.
  55. ;
  56. ;   Parallel port usage:
  57. ;
  58. ;   DATA PORT   : used for data output only
  59. ;   STATUS PORT : used for data & control input
  60. ;   CONTROL PORT: used for control output
  61. ;
  62. ;   All lines pulled up.  Thus, asserted state is a 0.    Idle
  63. ;   state is an undriven (1).  Protocol transfers a byte at
  64. ;   a time.  Protocol is ethernet style with a small window
  65. ;   of error in the line aquisition routine.
  66. ;
  67. ;   Note:   Timeouts should be set around a second.  Ideally
  68. ;        defaults should be fixed on faster machines.
  69. ;
  70. ;   LINE description:
  71. ;
  72. ;   PARnet: Input: Output:  Description:
  73. ;   ---------------------------------------------------------
  74. ;   ~ACK    SLCT   STROBE   hand shake
  75. ;   ~REQ    POUT   AUTOLF   hand shake
  76. ;    CTL    ACK    INIT     1 = special byte, 0 = data byte.
  77. ;                (for address mark, valid when ~REQ
  78. ;                 goes low.    For EOP mark, sample
  79. ;                 when ~REQ goes high)
  80. ;
  81. ;   See the associated routines for the exact low-level protocols.
  82. ;
  83. ;
  84. ;   PROTOCOL DESCRIPTION:
  85. ;
  86. ;   HandShake    (Reader <- Writer transfer).  A Handshake
  87. ;        sequence transfers TWO bytes of information.
  88. ;
  89. ;        WRITER                READER
  90. ;    |-> place data, ~REQ->0
  91. ;    |                 wait for ~REQ->0
  92. ;    |                 read data & store
  93. ;    |                 set ~ACK->0
  94. ;    |   wait ~ACK->0
  95. ;    |   place data, ~REQ->1
  96. ;    |                 wait for ~REQ->1
  97. ;    |                 read data & store
  98. ;    |                 set ~ACK->1
  99. ;    |   wait ~ACK->1
  100. ;    |<- LOOP  (2 bytes written)      LOOP  (2 bytes read)
  101. ;
  102. ;
  103. ;   Read:   (1) Determine if your machine is being addressed
  104. ;        (~REQ=0, data=myaddress, CTL=1)
  105. ;        (1) set ~ACK to output and
  106. ;        (2) Handshake sequence for the address mark, only
  107. ;        first byte valid.
  108. ;        (3) Handshake sequence for data util rcv byte
  109. ;        with CTL = 1 (EOP), byte must == 0.
  110. ;        (4) Set ~ACK to input
  111. ;
  112. ;   Write:  (1) AQUIRE THE NETWORK (see below)
  113. ;        involves gaining control and then setting the
  114. ;        CTL and ~REQ to outputs.
  115. ;
  116. ;        Also checks if somebody is writing to us,
  117. ;        in which case -2 is returned instantaniously
  118. ;        indicating we should do a ParRead().
  119. ;
  120. ;        (2) Handshake sequence for address mark, send dest
  121. ;        address (second byte garbage).  Note that CTL->1
  122. ;        *BEFORE* we set ~REQ->0
  123. ;        (3) Handshake sequence for data bytes
  124. ;        (4) Handshake sequence for EOP mark (Note that CTL->1
  125. ;        *AFTER* we get the ~ACK->1 and before release
  126. ;        ~REQ (->1).  Only firstbyte valid and set to 0.
  127. ;
  128. ;        (5) Set ~ACK and ~CTL to inputs
  129. ;
  130. ;   AQUIRE: Line aquisition prevents two people from writing to
  131. ;        the net at the same time.
  132. ;
  133. ;        * A line is considered aquired if ANY of the 3
  134. ;          control lines is 0.
  135. ;        * If network is not aquired:
  136. ;        - set ~ACK to output and 1
  137. ;        - bclr ~ACK to 0 and bne success
  138. ;          (else set ~ACK to input and try again)
  139. ;        - set data lines to output and place my address
  140. ;          (Must ]be done before CTL is glitched)
  141. ;        - set ~CTL to output and 1
  142. ;        - set CTL to 0 and then 1 (glitch it) to cause
  143. ;          FLAG interrupt on all other machines
  144. ;        - set ~REQ to output and 0 (beginning of handshake
  145. ;          sequence)
  146. ;        - lastly, release ~ACK by setting it to an input
  147. ;
  148. ;          Note that at all times at least one line is a 0
  149. ;          so no other machine will attempt to aquire the net.
  150. ;
  151. ;   Note that the destination address is placed on the data
  152. ;   lines after we have aquired the line but before we glitch
  153. ;   the CTL line to cause an interrupt.  This allows the
  154. ;   other machines to instantaniously determine who is being
  155. ;   addressed.
  156. ;
  157. ;   Note that the CTL line gets glitched at the end of a packet
  158. ;   too for the EOP mark.  In this case there is a 0 on the
  159. ;   data lines so while an interrupt is generated, nobody
  160. ;   thinks they are being addressed.
  161. ;
  162.  
  163. Par_EthAddr     db 0,0,0,0,0    ; to align the 6 octets of the fake
  164.                 ; PARnet "ethernet" address.
  165. ParNetAddr    db 1,0,0,0    ; my PARnet address (default), the trailing
  166.                 ; 3 zero's are padding for the get_number
  167.                 ; function.
  168.  
  169. LPTData        dw 0378h    ; LPT data port address (default)
  170. LPTStatus    dw 0379h    ; LPT status port address (default)
  171. LPTControl    dw 037ah    ; LPT control port address (default)
  172.  
  173. ParLLTimeout    dw 65535
  174.     ;should be dl 983025    ; default timeout value (about 1 second?)
  175. Dummybuf    db 0        ; dummy buffer
  176.         db 0        ; dummy buffer
  177.         db 0,0        ; padding
  178.  
  179. ; Parnet interface truth table
  180. ;
  181. ; 0 = line is 0V
  182. ; 1 = line is 5V
  183. ; X = don't care
  184. ;
  185. ;SELIN  D3 STROBE | RD RC WD WC | Function
  186. ;-----------------+-------------+---------------
  187. ; 0      0    X   | 0  1  1  1    | Read Data
  188. ; 0      1    X   | 1  1  1  0    | Write Control
  189. ; 1      X    0   | 1  1  0  1    | Write Data
  190. ; 1      X    1   | 1  0  1  1    | Read Control
  191. ;
  192.  
  193. ; Put the interface in a stable mode by switching it into
  194. ; 'Read Control' mode.
  195. ;
  196. stable: push ax
  197.     push dx
  198.     mov dx,LPTControl    ;control register 2 (to clock in data & Read Control)
  199.  
  200.     mov al,14h    ;all outputs to 1 (INIT is not inverted!), enable IRQ7
  201.     out dx,al    ;STROBE=1 & SELIN=1 -> read control (stable mode).
  202.     pop dx
  203.     pop ax
  204.     ret
  205.  
  206. ; Set the datalines to 'input' by setting all lines to high.
  207. ; (Open collector outputs not active).
  208. ;
  209. data_input:
  210.     push ax
  211.     push dx
  212.     mov dx,LPTData    ;data register
  213.     mov al,0ffh    ;datalines high, so
  214.     out dx,al    ;open collector drivers not active.
  215.     mov dx,LPTControl    ;control register 2 (to set data)
  216.  
  217.     mov al,15h    ;SELIN=1 & STROBE=0 Enable IRQ7
  218.     out dx,ax    ;write data
  219.     call Stable    ;be sure to CLOCK it in.
  220.     pop dx
  221.     pop ax
  222.     ret
  223.  
  224. ; Set the control-lines to 'input' by setting all lines to high.
  225. ; (Open collector outputs not active).
  226. ;
  227. control_input:
  228.     push ax
  229.     push dx
  230.     mov dx,LPTData    ;data register
  231.     mov al,0ffh    ;d3=1, rest don't care
  232.     out dx,ax
  233.     mov dx,LPTControl ;Control register
  234.  
  235.     mov al,1ch    ;all outputs to 1, SELIN to 0 (INIT is not inverted!)
  236.     out dx,al    ;d3=1 & SELIN=0 -> write control
  237.     call Stable    ;be sure to CLOCK it in.
  238.     pop dx
  239.     pop ax
  240.     ret
  241.  
  242. ; read data from the cable
  243. read_data:        ;buffers must be in stable & data_input mode.
  244.     push ax
  245.     push bx
  246.     push dx
  247.     mov dx,LPTData    ;data register
  248.     mov al,00    ;d3=0
  249.     out dx,al    ;all datalines to zero.
  250.     mov dx,LPTControl ;Control register
  251.  
  252.     mov al,1ch    ;SELIN=0, rest high (INIT is not inverted!).
  253.     out dx,al    ;d3=0 & SELIN=0 -> Read Data.
  254.     mov dx,LPTStatus ;status register
  255.     in al,dx    ;read it
  256.     mov bl,al    ;save it to BL (AL will be changed)
  257.     mov dx,LPTControl    ;control register 2
  258.     in al,dx    ;read it
  259.     mov cl,al    ;save it to CL
  260.     call stable    ;put all buffers in tri-state mode.
  261.     call data_input ;a bug, the change from RD->RC could activate WD.
  262.     call translate_data ; put data into 8 bit CL.
  263.     pop dx
  264.     pop bx
  265.     pop ax
  266.     ret
  267.  
  268. ; Translation incomming data via control lines.
  269. ; (some lines are inverted, and I can't read 8 bits at once)
  270. ;
  271. ; Let's say, all datalines on the cable are high:
  272. ;
  273. ; databits: 7654 3210
  274. ; value:    1111 1111
  275. ;
  276. ; Then the input from the control registers would look like this:
  277. ;
  278. ; CL: data read from control register 2
  279. ;
  280. ; CL bits: 7654 3210  centronics  corresponding
  281. ; value:   XXXX X100   keyword:    databits:
  282. ;          |||| |||`- strobe         d0
  283. ;          |||| ||`-- auto LF        d1
  284. ;          |||| |`--- init           d2
  285. ;          |||| `---- invalid        x
  286. ;          ````------ invalid        x
  287. ;
  288. ; BL: data read from control register 1
  289. ;
  290. ; BL bits: 7654 3210  centronics  corresponding
  291. ; value:   0111 1XXX   keyword:     databits:
  292. ;          |||| |```- invalid          x
  293. ;          |||| `---- error           d3
  294. ;          |||`------ select          d4
  295. ;          ||`------- paper empty     d5
  296. ;          |`-------- ack             d6
  297. ;          `--------- busy            d7
  298. ;
  299. translate_data:
  300.     xor cl,03h    ;invert bits 0 and 1
  301.     and cl,07h    ;discards bits not needed in CL.
  302.     xor bl,80h    ;invert bit 7 in BL (busy).
  303.     and bl,0f8h    ;discard bits not needed in BL.
  304.     or cl,bl    ;merge them together (high bits in BL, lower in CL)
  305.     ret        ;ready, translated data in CL.
  306.  
  307.  
  308. ; Read the controlbits from the cable (busy, pout & sel)
  309. ;
  310. ; BL: control read from control register 1, represents the REAL line
  311. ;     status.
  312. ;
  313. ; BL bits: 7654 3210  centronics:    cable:  parnet:
  314. ; value:   0111 1XXX   
  315. ;          |||| |```- invalid          x
  316. ;          |||| `---- error            x
  317. ;          |||`------ select          busy    ack
  318. ;          ||`------- paper empty     pout    req
  319. ;          |`-------- ack             sel     ctl
  320. ;          `--------- busy             x
  321. ;
  322. ;Output: BL, but shifted 4 bits to the right.
  323. ;
  324. read_control:            ;control lines must be set to input first.
  325.     push ax
  326.     push cx
  327.     push dx
  328.     call Stable        ;Stable mode = Read_Control mode.
  329.     mov dx,LPTStatus    ;Status register
  330.     in al,dx        ;read it
  331.     mov cl,04        ;need CL to shift AL
  332.     shr al,cl        ;shift 4 bits to the right.
  333.     mov bl,al        ;save it to BL
  334.     and bl,07        ;discard bits not needed.
  335.     pop dx
  336.     pop cx
  337.     pop ax
  338.     ret            ;ready, controlbits in BL.
  339.  
  340. ;-----------------------------------------------------------------------------
  341. ; Clear the parnet ack-bit. Leave other control lines high.
  342. ; Warning: I can't read my own control-lines back!
  343. ;
  344. clear_ack_only:
  345.     push bx
  346.     push cx
  347.     mov bl,0feh        ; set parnet ack-bit to 0
  348.     call write_control    ; write control
  349.     pop cx
  350.     pop bx
  351.     ret
  352.  
  353. ;-----------------------------------------------------------------------------
  354. ; Set the parnet ack-bit. Leave other control lines high.
  355. ; Warning: I can't read my own control-lines back!
  356. ;
  357. Set_ack_all:
  358.     push bx
  359.     push cx
  360.     mov bl,0fh        ; set all bits to 1 (including ACK)
  361.     call write_control    ; write control
  362.     pop cx
  363.     pop bx
  364.     ret
  365.  
  366. ; Write control
  367. ;
  368. ; BL: controlbits to be written
  369. ;
  370. ; BL bits: 7654 3210                centronics
  371. ; value:   XXXX X100  controlbits:  keyword:
  372. ;          |||| |||`-    ack        strobe
  373. ;          |||| ||`--    req        auto LF
  374. ;          |||| |`---    ctl        init
  375. ;          |||| `----     x         selin
  376. ;          ````------     x         invalid
  377. ;
  378. ; !The value in BL represents the REAL LINE STATUS, so a bit=0 means the
  379. ; !line is on low voltage.
  380. ;
  381. ; The INIT output is on the parallel card not inverted. This bit will be
  382. ; inverted in this procedure, so you don't have to care about it.
  383. ;
  384. write_control:        ; Interface must be in stable mode!
  385.     push ax
  386.     push dx
  387.     mov dx,LPTData    ; data register
  388.     mov al,0ffh    ; d3=1, rest don't care.
  389.     out dx,al    ;
  390.     mov dx,LPTControl ; control register
  391.     mov al,bl    ; move control bits to AL (for OUT-instruction)
  392.     xor al,0bh    ; invert all lines except for INIT (=ctl)
  393.     or al,08h    ; SELIN = 0 (inverted!)
  394.     out dx,al    ; SELIN = 0 & d3=1 -> write control.
  395.     call Stable
  396.     pop dx
  397.     pop ax
  398.     ret
  399.  
  400. ; Write DATA
  401. ;
  402. ; CL: data to be written.
  403. ;
  404. write_data:            ; interface must be in stable mode!
  405.     push ax
  406.     push dx
  407.     mov dx,LPTData        ; data register
  408.     mov al,cl        ; move data to AL for OUT-instruction.
  409.     out dx,al        ; put it on (not on cable yet).
  410.     mov dx,LPTControl    ; control register
  411.     mov al,15h        ; SELIN=1, strobe=0, enable IRQ7
  412.     out dx,al        ; write data
  413.     call Stable        ; be sure to 'CLOCK' it in.
  414.     pop dx
  415.     pop ax
  416.     ret
  417.  
  418.  
  419. ; Function: paraddress
  420. ; Parameters: io_addr
  421. ;
  422. ; Set my LPT port address (0378h,03bch or 0278h)
  423. ;
  424. paraddress:
  425.     mov ax,io_addr        ; LPT port address
  426.     mov LPTData,ax        ; Place data port address
  427.     inc ax            ; next register is status port
  428.     mov LPTStatus,ax
  429.     inc ax            ; next register is control port
  430.     mov LPTControl,ax
  431.     call data_input        ; data lines high
  432.     call control_input    ; control lines high
  433.     ret
  434.  
  435. ; Function: pardataready
  436. ; Parameter: none
  437. ;
  438. ; Check for data present (e.g. after an IRQ7).
  439. ;
  440. ; Returns: AL =  1 if packet is probably pending for you
  441. ;          AL =  0 if line is currently idle
  442. ;          AL = -1 if packet isn't for you
  443. ;
  444. ; If line has been aquired but no control address has been
  445. ; put on it yet, pardataready() will wait for a control
  446. ; address.  Thus, after a signal, a single call to
  447. ; pardataready() should suffice.
  448. ;
  449. pardataready:
  450.     call data_input        ; be sure all lines
  451.     call control_input    ; are set to input.    
  452. .pdstable:
  453.     call read_control    ; read control in BL
  454.     mov al,bl        ; save it
  455.     call read_data        ; read data in CL
  456.     call read_control    ; read control in BL
  457.     cmp al,bl
  458.     jne .pdstable
  459.  
  460.     ;   Now, pardataready might be called after the sending machine
  461.     ;   has aquired but before it can assert REQ.  However, the
  462.     ;   sending machine has already (guarenteed) placed its address
  463.     ;   on the data port.  So while the address matches, loop while
  464.     ;   REQ not asserted.
  465.  
  466.     test bl,02        ; ~REQ asserted?
  467.     jz .pd10        ; yes
  468.     cmp  [ParNetAddr],cl    ; no, does data match anyway?
  469.     je .pdstable        ; YES, loop until get ~REQ or
  470.     jmp .pdfail        ; data bad.
  471.  
  472. .pd10:
  473.     test bl,04        ; yes, CTL?
  474.     jz .pdrn        ; no, middle of some packet.
  475.     cmp [ParNetAddr],cl    ; yes, my address?
  476.     jne .pdrn        ; nope
  477.     mov al,1        ; yes, packet (probably) for us.
  478.     ret
  479.  
  480. .pdfail:
  481.     test bl,04        ; fail due to ~REQ not asserted.
  482.     jz .pdrn        ; CTL=0, line busy
  483.     mov al,0        ; line idle.
  484.     ret
  485.  
  486. .pdrn:
  487.  
  488.     mov al,-1        ; line busy, packet not for me.
  489.     ret
  490.  
  491. ;Function: parread
  492. ;Parameters: rx_buffer
  493. ;            rx_length
  494. ;
  495. ;Read a pending packet.
  496. ;
  497. ;Returns: AX = -1 (1 second timeout, no packet pending)
  498. ;       AX = 0 to bytes-1 (1 second timeout after transmission interrupted)
  499. ;      AX = bytes (success), or n > bytes (transmitting machine's packet
  500. ;          was larger than we can handle, extra bytes thrown out)
  501. ;
  502. ;NOTE:    Requesting an odd number of bytes is O.K. but if you request N where
  503. ;    N is odd and the writer sends N + 1 you will never know (N will be
  504. ;    returned). See also parwrite() below.
  505. ;
  506. parread:
  507.     push si
  508.     push di
  509.         mov di,OFFSET rx_buffer  ; ds:di = buf (ds already set)
  510.     mov si,BUFSIZE        ; maximum number of bytes to read.
  511.  
  512.     call data_input        ; ensure all
  513.     call control_input    ; are inputs.
  514.     call Stable        ; ensure line not asserted.
  515.     mov DX,[ParLLTimeout]    ; DX = timeout load
  516.  
  517.     ;  Wait loop for address mark
  518.     ;  Ctl = 1, ~DReq = 0
  519. .rmstab:
  520.     call read_control    ; read control in BL
  521.     mov al,bl        ; save it
  522.     call read_data        ; read data in CL
  523.     call read_control    ; read control in BL
  524.     cmp al,bl        ; control lines stable?
  525.     jne .rmstab        ; nope
  526.     test al,04        ; expect CTL=1
  527.     jz .rms1        ; nope
  528.     test al,02        ; expect ~REQ=0
  529.     jz .rms2        ; yes
  530.  
  531. .rms1:
  532.     dec dx            ; decrement timeout count
  533.     jnz .rmstab        ; no timeout.
  534.         mov ax,-1
  535.     jmp .rmend        ; no address mark!
  536.  
  537. .rms2:
  538.     cmp [ParNetAddr],CL    ; My address?
  539.     jne .rms1        ; no, timeout loop
  540.  
  541.     ; Got my address, ~Ack byte.
  542.  
  543.     mov DX,[ParLLTimeout]    ; reset timeout
  544.     call Clear_Ack_Only    ; set ~Ack to 0
  545.  
  546. .rms4:  call read_control    ; get control
  547.     test bl,02        ; wait for ~REQ to go away
  548.     jnz .rms5
  549.     dec dx            ; decrement timeout count
  550.     jnz .rms4
  551.         mov ax,-2               ;~REQ not released?
  552.     jmp .rmend
  553.  
  554. .rms5:  call Set_Ack_All    ; release ~ACK
  555.  
  556.     mov ax,0        ; set # of bytes read to 0
  557.     jmp .rms10        ; skip past move
  558.  
  559.  
  560.     ; MAIN READ LOOP
  561.     ;
  562.     ; AX holds count (number of bytes read).
  563.     ; DI buffer pointer (place to store data).
  564.  
  565. .rms10loop:
  566.         mov [di],cl        ; store data
  567.     inc di            ; next address
  568.                 ; (can't do this with a STOSB)
  569.  
  570. .rms10:
  571.     call Read_Control
  572.     test bl,02        ; wait for ~REQ asserted
  573.     jz .rms20
  574.     call Read_control
  575.     test bl,02        ; again
  576.     jz .rms20
  577.     mov DX,[ParLLTimeout]    ; reset timeout
  578.  
  579. .rms11: call Read_Control
  580.     test bl,02        ; wait for ~REQ asserted with timeout
  581.     jz .rms20
  582.     dec dx            ; decrement timeout count
  583.     jnz .rms11
  584.         mov ax,-1
  585.     jmp .rmend        ; timeout
  586.  
  587. .rms20: call read_data        ; get data in CL and
  588.     call Clear_Ack_Only    ; assert ~ACK
  589.  
  590.     ; note on CTL = 1 end sequence this data item is a dummy
  591.  
  592.         mov [di],cl          ; store data
  593.     inc di            ; next address
  594.     inc ax            ; optimized, but not quite true,
  595.     inc ax            ; we've only written one 1 sf.
  596.  
  597.     call Read_Control
  598.     test bl,02        ; wait for ~REQ released
  599.     jnz .rms30
  600.     call Read_Control
  601.     test bl,02        ; again
  602.     jnz .rms30
  603.     mov DX,[ParLLTimeout]    ; reset timeout
  604. .rms21:
  605.     call Read_Control
  606.     test bl,02        ; wait for ~REQ released with timeout
  607.     jnz .rms30
  608.     dec dx            ; decrement timeout count
  609.     jnz .rms21        ; no timeout yet?
  610.     jmp .rmendsub        ; sub because D6 is 2 ahead
  611.  
  612.  
  613. .rms30:
  614.     call read_data        ; get data in CL
  615.     call Read_Control    ; get CTL status in BL
  616.     call Set_Ack_All    ; release ~ACK    
  617.     test bl,04        ; EOP if CTL=1
  618.     jnz .rmeop
  619.  
  620.     ; CANNOT STORE DATA HERE! In case odd # bytes requested,
  621.     ; second byte would overflow buffer (each handshake sequence ALWAYS
  622.     ; transfers 2 bytes of information)
  623.  
  624.     dec  si            ; # of bytes remaining
  625.     jz   .rmodd        ; already zero, # of bytes were odd.
  626.     dec  si
  627.  
  628.     jz  .rmste        ; reached zero (even bytes)
  629.     jmp .rms10loop        ; continue if >0.
  630.  
  631. .rmodd: dec si            ; si must be -1 when odd # bytes requested.
  632.     dec ax            ; fixup count (was one ahead)
  633.     jmp .rmeven
  634.  
  635. .rmste:
  636.         mov [di],CL        ; if si = 0, its's even and we
  637.                 ; should store the last byte.
  638. .rmeven:
  639. .rmovflow:
  640.     mov di,OFFSET Dummybuf    ; overflow, use dummy buffer
  641.  
  642.     jmp .rms10
  643.  
  644. .rmeop:
  645.     cmp CL,0        ; EOP data better be 0!
  646.     je .rmendsub
  647.         mov ax,-3               ; bad protocol
  648.     jmp .rmend
  649.  
  650. .rmendsub:
  651.     dec ax            ; because we were two ahead
  652.     dec ax
  653. .rmend:
  654.     call data_input
  655.     call Set_Ack_All    ; setting ~ACK to input
  656.     call Stable
  657.     pop di
  658.     pop si
  659.     ret            ; return value in AL
  660.  
  661. ;Function: parwrite
  662. ;Parameters: tx_dest
  663. ;            tx_header
  664. ;            tx_hdrsize (=8)
  665. ;            tx_buffer
  666. ;            tx_dlen
  667. ;
  668. ;Write a packet.
  669. ;
  670. ;Returns: AX = -2       Cannot write anything, a packet is pending
  671. ;            (instantanious)
  672. ;         AX = -1       Destination machine does not respond (1 sec timeout)
  673. ;         AX = N        N bytes written ok (success if n == bytes)
  674. ;
  675. ; NOTE: sending an odd number of bytes is O.K. but if you write N where
  676. ;    N is odd and the reader requests N + 1 he will get N + 1 the last
  677. ;    byte being garbage.
  678. ;
  679. parwrite:
  680.     push es
  681.     push si
  682.     push di
  683.     mov ax,ds
  684.     mov es,ax        ; to simulate mov es,ds
  685.         mov si,OFFSET tx_header    ; es:si = buf
  686.  
  687.     mov di,tx_hdrsize    ; number of bytes to write.
  688.  
  689.  
  690.     call data_input        ; ensure all
  691.     call control_input    ; are inputs.
  692.     call Stable        ; ensure line not asserted.
  693.     mov DX,[ParLLTimeout]    ; DX = timeout load
  694.  
  695. .wmstab:
  696.     cli            ; disable interrupts
  697.     call Read_Control
  698.     mov al,bl
  699.     call read_data    
  700.     call Read_Control    ; get stable control
  701.     cmp al,bl
  702.     je .wmstab1
  703.     sti            ; set interrupts
  704.     jmp .wmstab
  705.  
  706.     ; Interrupts still disabled
  707.     ; BX holds ~ACK ~REQ and CTL status
  708.  
  709. .wmstab1:
  710.     mov ax,-2        ; number of bytes written yet (=none).
  711.     cmp bl,07h        ; ~ACK=1, ~REQ=1, CTL=1 ?
  712.     je .wm02
  713.  
  714.     ; if CTL=1, ~REQ=0 and CL=my address then
  715.     ; return with -2
  716.  
  717.     test bl,02h        ; ~REQ = 0?
  718.     jne .wm01        ; nope
  719.     test bl,04h        ; CTL = 1?
  720.     je .wm01        ; nope
  721.     cmp [ParNetAddr],cl    ; somebody is calling me?
  722.     jne .wm01        ; nope
  723.  
  724.     sti            ; enable interrupts
  725.     jmp .wmend
  726.  
  727. .wm01:
  728.     sti            ; enable interrupts
  729.     dec dx            ; decrement timeout count
  730.     jnz .wmstab
  731.     jmp .wmend
  732.  
  733.     ; interrupts still disabled
  734.     ; we almost own the line
  735.  
  736. .wm02:
  737.     call Read_Control
  738.     call Clear_Ack_Only    ; assert ~ACK
  739.     test bl,01
  740.     jne .wm05        ; ACK was released before, have line!
  741.  
  742.     call Set_Ack_All    ; release ~ACK
  743.     jmp .wm01
  744.  
  745.     ; Line now aquired.
  746.  
  747. .wm05:
  748.     sti            ; enable interrupts
  749.         mov cl,tx_dest
  750.     call Write_Data        ; set destination address on datalines.
  751.  
  752.     ; Before asserting ~REQ, pulse CTL to cause interrupt on remote
  753.     ; machines. Note that our address is already on the datalines.
  754.  
  755.     mov bl,02h        ; leave ~ACK=0 and ~REQ=1, but CTL->0
  756.     call Write_Control
  757.     mov bl,06h        ; leave ~ACK=0 and ~REQ=1, but CTL->1
  758.     call Write_Control
  759.     mov bl,04h        ; leave ~ACK=0 and CTL=1, but ~REQ->0  
  760.     call Write_Control    ; assert REQ
  761.     mov bl,05h        ; leave CTL=1 and ~REQ=0, but ~ACK->1
  762.     call Write_Control    ; release ACK
  763.                 ; (note that REQ->0 before ACK->released)
  764.  
  765.     mov ax,-1        ; number of bytes written yet (none).
  766.  
  767.     ; interrupts enabled for transfer (fully handshaked)
  768.     ;
  769.     ; Address mark ~ACK, wait for ~ACK asserted.
  770. .wm10:
  771.     call Read_Control
  772.     test bl,01        ; ~ACK asserted?
  773.     jz .wm15        ; yes, remote machine got my address mark
  774.     mov DX,[ParLLTimeout]    ; DX = timeout count
  775. .wm11:
  776.     call Read_Control
  777.     test bl,01
  778.     jz .wm15
  779.     dec dx            ; decrement timeout count
  780.     jnz .wm11        ; timeout?
  781.     jmp .wmend        ; yes.
  782.  
  783.     ; got ack, now set CTL = 0 (leaves at least one line 0 so
  784.     ; nobody else thinks the bus is idle!)
  785.     ;
  786.     ; note:  Since this is the address mark, and is sampled by
  787.     ;     the reader before it asserts ~ACK, I can set CTL
  788.     ;     = 0 now instead of waiting till after ~ACK is
  789.     ;     released.
  790.  
  791. .wm15:
  792.     mov bl,01h        ; leave ACK=1, REQ=0, but CTL->0
  793.     call Write_Control    ; set CTL = 0 for duration of packet
  794.     mov bl,03h        ; leave ACK=1, CTL=0, but REQ->1
  795.     call Write_Control    ; release ~REQ
  796.  
  797.     call .wm19        ; Send header
  798.     cmp ax,tx_hdrsize    ; all bytes written?
  799.     je .wm16        ; yes, ok
  800.     jmp .wmend        ; no, then exit with error
  801. .wm16:  
  802.         les si,tx_data      ; es:si = buf
  803.         mov di,tx_length        ; number of bytes to write.
  804.     call .wm19        ; Send data
  805.     cmp ax,tx_length    ; all bytes written?
  806.     je .wm17        ; yes, ok
  807.     jmp .wmend        ; no, then exit with error
  808. .wm17:
  809.         jmp .wm50a              ; all bytes transmitted, send an EOP.
  810.  
  811.  
  812.     ; Data transfer loop
  813.     ;
  814.     ; wait for ~ACK to be released (-> 1). If no more bytes to
  815.     ; write, then skip to .wm50
  816.  
  817. .wm19:
  818.     mov ax,0        ; number of bytes written (none).
  819.  
  820. .wm20:
  821.     or  di,di        ; more data in this buffer?
  822.     jz .wm50        ; nope
  823.  
  824.     call Read_Control
  825.     test bl,01        ; wait for ~ACK to be released
  826.     jnz .wm30
  827.     mov DX,[ParLLTimeout]    ; DX = timeout countdown
  828. .wm21:
  829.     call Read_Control
  830.     test bl,01
  831.     jnz .wm30        ; need the timeout here?
  832.     call Read_control
  833.     test bl,01        ; check ACK again
  834.     jnz .wm30
  835.     dec dx
  836.     jnz .wm21
  837.     ret            ; timeout
  838.  
  839.     ; Assert ~REQ for this data byte and wait for ~ACK
  840.  
  841. .wm30:
  842.         mov cl,es:[si]        ; get next data byte.
  843.     inc si
  844.     call Write_Data        ; store data and ..
  845.     mov bl,01h        ; leave ACK=1, CTL=0, but REQ->0
  846.     call Write_Control    ; .. assert ~REQ
  847.  
  848.         mov cl,es:[si]          ; get next data bytes
  849.     inc si
  850.     inc ax            ; number of bytes written (this only)
  851.                 ; (not valid until we get ACK which
  852.                 ;  is why the wmendsub is included)
  853.  
  854.     call Read_Control
  855.     test bl,01        ; wait for ACK asserted
  856.     jz .wm40
  857.     call Read_Control
  858.     test bl,01        ; look again
  859.     jz .wm40
  860.     mov DX,[ParLLTimeout]    ; DX = timeout count
  861. .wm31:
  862.     call Read_Control
  863.     test bl,01        ; wait for ACK asserted with timeout
  864.     jz .wm40
  865.     dec dx            ; decrement timeout count
  866.     jnz .wm31
  867.     dec ax            ; timeout, AX was ahead in count
  868.     ret            ; timeout
  869.  
  870.     ; have ~ACK, so byte transmitted. increment bytes written,
  871.     ; decr. of bytes left was already done before.
  872.     ; now send second byte and loop back.
  873.  
  874. .wm40:    call Write_Data        ; data was loaded in CL before.
  875.     mov bl,03h        ; leave ACK=1, CTL=0, but REQ->1
  876.     call Write_Control    ; release ~REQ
  877.     inc ax            ; increment number of bytes written
  878.  
  879.     dec di            ; one less bytes
  880.     jz .wm40a        ; odd number of bytes were written
  881.     dec di            ; one less bytes
  882.     jz .wm50        ; these were the last even bytes(s)
  883.  
  884.     jmp .wm20        ; loop back
  885.  
  886. .wm40a: dec di            ; fixup, di = -1 when odd bytes requested
  887.  
  888.     ; Last byte in this buffer has been transmitted.
  889.     ; Get next buffer.
  890.  
  891.         add ax,di               ; adjust AX when odd bytes requested
  892. .wm50:  ret
  893.  
  894.  
  895.  
  896. .wm50a:
  897.     ;   Last byte has been transmitted,
  898.     ;
  899.     ;   Wait for ~ACK to be released and then assert ~REQ with
  900.     ;   EOP & CTL = 1
  901.     ;
  902.     ;   (timing on read is that CTL is sampled when ~REQ is
  903.     ;   RELEASED so no timing window here)
  904.  
  905.     call Read_Control    ; Wait ~ACK released
  906.     test bl,01
  907.     jz .wm50a        ; loop back
  908.  
  909.     mov cl,0
  910.     call Write_Data        ; EOP mark (=0)
  911.     mov bl,01h        ; leave ACK=1 and CTL=0, but REQ->0
  912.     call Write_Control    ; assert ~REQ
  913.  
  914.     ; Wait for ~ACK asserted
  915.  
  916.     call Read_Control
  917.     test bl,01        ; ACK asserted?
  918.     jz .wm60        ; yes
  919.     mov DX,[ParLLTimeout]    ; DX = timeout count
  920. .wm51:
  921.     call Read_Control
  922.     test bl,01        ; wait for ACK asserted with timeout
  923.     jz .wm60
  924.     dec dx            ; decrement timeout count
  925.     jnz .wm51
  926.     mov ax,-3        ; EOP failed
  927.     jmp .wmend
  928.  
  929.     ; Set CTL=1 then release ~REQ, then wait for ~ACK released
  930. .wm60:
  931.     mov bl,05h        ; leave ACK=1, REQ=0, but CTL->1
  932.     call Write_Control    ; release CTL
  933.     mov bl,07h        ; leave ACK=1, CTL=1, but REQ->1
  934.     call Write_Control    ; release ~REQ
  935.     
  936.     ; Wait ~ACK released ?
  937.  
  938. .wm61:    call Read_Control
  939.     test bl,01
  940.     jz .wm61
  941.  
  942.     ; Add DI to AX. This handles fixup if an odd number of bytes were
  943.     ; requested written, DI will be -1 (odd) or 0 (even) and AX will 
  944.     ; be one too large (odd) or perfect (even)
  945.  
  946. ;        add ax,di
  947.  
  948. .wmend:
  949.     call data_input        ; set data port to input
  950.     call control_input    ; set control lines to input
  951.     pop di
  952.     pop si
  953.     pop es
  954.     ret            ; return value in AL
  955.  
  956.